<!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>Javascript&#x57fa;&#x7840; - &#x7c7b;&#x578b;&#x57fa;&#x7840;</title>
            <style>
/* From extension vscode.github */
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

.vscode-dark img[src$=\#gh-light-mode-only],
.vscode-light img[src$=\#gh-dark-mode-only] {
	display: none;
}

</style>
            
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/markdown.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/highlight.css">
<style>
            body {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif;
                font-size: 14px;
                line-height: 1.6;
            }
        </style>
        <style>
.task-list-item {
    list-style-type: none;
}

.task-list-item-checkbox {
    margin-left: -20px;
    vertical-align: middle;
    pointer-events: none;
}
</style>
<style>
:root {
  --color-note: #0969da;
  --color-tip: #1a7f37;
  --color-warning: #9a6700;
  --color-severe: #bc4c00;
  --color-caution: #d1242f;
  --color-important: #8250df;
}

</style>
<style>
@media (prefers-color-scheme: dark) {
  :root {
    --color-note: #2f81f7;
    --color-tip: #3fb950;
    --color-warning: #d29922;
    --color-severe: #db6d28;
    --color-caution: #f85149;
    --color-important: #a371f7;
  }
}

</style>
<style>
.markdown-alert {
  padding: 0.5rem 1rem;
  margin-bottom: 16px;
  color: inherit;
  border-left: .25em solid #888;
}

.markdown-alert>:first-child {
  margin-top: 0
}

.markdown-alert>:last-child {
  margin-bottom: 0
}

.markdown-alert .markdown-alert-title {
  display: flex;
  font-weight: 500;
  align-items: center;
  line-height: 1
}

.markdown-alert .markdown-alert-title .octicon {
  margin-right: 0.5rem;
  display: inline-block;
  overflow: visible !important;
  vertical-align: text-bottom;
  fill: currentColor;
}

.markdown-alert.markdown-alert-note {
  border-left-color: var(--color-note);
}

.markdown-alert.markdown-alert-note .markdown-alert-title {
  color: var(--color-note);
}

.markdown-alert.markdown-alert-important {
  border-left-color: var(--color-important);
}

.markdown-alert.markdown-alert-important .markdown-alert-title {
  color: var(--color-important);
}

.markdown-alert.markdown-alert-warning {
  border-left-color: var(--color-warning);
}

.markdown-alert.markdown-alert-warning .markdown-alert-title {
  color: var(--color-warning);
}

.markdown-alert.markdown-alert-tip {
  border-left-color: var(--color-tip);
}

.markdown-alert.markdown-alert-tip .markdown-alert-title {
  color: var(--color-tip);
}

.markdown-alert.markdown-alert-caution {
  border-left-color: var(--color-caution);
}

.markdown-alert.markdown-alert-caution .markdown-alert-title {
  color: var(--color-caution);
}

</style>
        
        </head>
        <body class="vscode-body vscode-light">
            <h1 id="javascript基础---类型基础">Javascript基础 - 类型基础</h1>
<h1 id="类型判断方法">类型判断方法</h1>
<p><code>JavaScript</code>是一种弱类型的动态语言，但弱类型不是没有类型，而是在运行期可以改变值的类型。可以使用以下方式检查值的类型。</p>
<h2 id="typeof">typeof</h2>
<p><code>typeof</code>操作符可获取指定值的类型名称：</p>
<pre><code class="language-javascript"><span class="hljs-keyword">typeof</span> <span class="hljs-literal">undefined</span>;   <span class="hljs-comment">// &#x27;undefined&#x27;</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-literal">null</span>;        <span class="hljs-comment">// &#x27;object&#x27; ***</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-literal">true</span>;        <span class="hljs-comment">// &#x27;boolean&#x27;</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-number">1</span>;           <span class="hljs-comment">// &#x27;number&#x27;</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-title class_">BigInt</span>(<span class="hljs-number">0</span>);   <span class="hljs-comment">// &#x27;bigint&#x27;（大整数）</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-string">&#x27;1&#x27;</span>;         <span class="hljs-comment">// &#x27;string&#x27; </span>
<span class="hljs-keyword">typeof</span> <span class="hljs-title class_">Symbol</span>();    <span class="hljs-comment">// &#x27;symbol&#x27; (ES6中新加入的类型)</span>
<span class="hljs-keyword">typeof</span> {};          <span class="hljs-comment">// &#x27;object&#x27;</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-title class_">Object</span>;      <span class="hljs-comment">// &#x27;function&#x27;</span>
</code></pre>
<p>注意<code>null</code>，按说<code>typeof null = 'null'</code>更合理一些，这可能是一个Bug，也可能受<code>Java</code>的<code>null</code>影响。</p>
<p><code>null</code>的历史最早可以追溯到1965年的<code>ALGOL</code>语言，作者<code>托尼·霍尔（Tony Hoare）</code>后来后悔了，认为他自己犯一个<code>至少价值十亿美元的错误，导致了无法计数的错误、漏洞和系统崩溃</code>，以致于新的语言完全摒弃了这一设计，比如<code>Rust</code>的<code>Option&lt;T&gt;</code>，一些较老的语言也加上了可空类型来进行补救，比如<code>C#</code>的<code>Nullable&lt;T&gt;</code>。</p>
<p><code>typeof</code>可以准确识别简单类型，但对于非函数对象的只能简单的返回<code>object</code>，需要获取对象的更详细的类型，可以使用<code>JavaScript</code>提供的其它方法。</p>
<h2 id="constructor属性">constructor属性</h2>
<p>几乎所有的对象都有一个<code>constructor</code>属性来指向对象的构造函数（类型）。不仅如此，布尔值、数值、字符串以及symbol值也一样可以访问这个属性：</p>
<pre><code class="language-javascript"><span class="hljs-literal">true</span>.<span class="hljs-property">constructor</span>;       <span class="hljs-comment">// Boolean</span>
(<span class="hljs-number">1</span>).<span class="hljs-property">constructor</span>;        <span class="hljs-comment">// Number</span>
<span class="hljs-string">&#x27;&#x27;</span>.<span class="hljs-property">constructor</span>;         <span class="hljs-comment">// String</span>
<span class="hljs-title class_">Symbol</span>().<span class="hljs-property">constructor</span>;   <span class="hljs-comment">// Symbol</span>
({}).<span class="hljs-property">constructor</span>;       <span class="hljs-comment">// Object</span>
(<span class="hljs-function">() =&gt;</span> {}).<span class="hljs-property">constructor</span>; <span class="hljs-comment">// Function</span>
[].<span class="hljs-property">constructor</span>;         <span class="hljs-comment">// Array</span>
</code></pre>
<p>对于自定义类型，也一样有效：</p>
<pre><code class="language-javascript"><span class="hljs-keyword">new</span> (<span class="hljs-keyword">function</span> <span class="hljs-title function_">CustomType</span>(<span class="hljs-params"></span>) {})().<span class="hljs-property">constructor</span>;   <span class="hljs-comment">// CustomType</span>
<span class="hljs-keyword">new</span> (<span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomType</span> {})().<span class="hljs-property">constructor</span>;        <span class="hljs-comment">// CustomType</span>
</code></pre>
<p>不过要注意，<code>constructor</code>属性值是可以修改的，</p>
<pre><code class="language-javascript"><span class="hljs-comment">// 修改对象的consturctor属性</span>
<span class="hljs-keyword">let</span> obj = <span class="hljs-keyword">new</span> (<span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomType</span> {})().<span class="hljs-property">constructor</span>;
obj.<span class="hljs-property">consturctor</span> = <span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span> {};
obj.<span class="hljs-property">consturctor</span>;    <span class="hljs-comment">// T</span>

<span class="hljs-comment">// 也可以修改函数或类的原型上的consturctor属性</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomType</span> {};
<span class="hljs-title class_">CustomType</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">constructor</span> = <span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span> {};
<span class="hljs-keyword">new</span> <span class="hljs-title class_">CustomType</span>().<span class="hljs-property">constructor</span>;   <span class="hljs-comment">// T</span>

<span class="hljs-comment">// 甚至可以把constructor修改为任意值</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomType</span> {};
<span class="hljs-title class_">CustomType</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">constructor</span> = <span class="hljs-number">12345</span>;
<span class="hljs-keyword">new</span> <span class="hljs-title class_">CustomType</span>().<span class="hljs-property">constructor</span>;   <span class="hljs-comment">// 12345</span>
</code></pre>
<p>修改后这个判断就不准确了，所以如果没有特殊需求，切记不要修改<code>constructor</code>属性。</p>
<h2 id="instanceof">instanceof</h2>
<p><code>instanceof</code>操作符可以判断一个对象是否某个类型或其子类型，但是对<code>null</code>、<code>boolean</code>、<code>number</code>、<code>string</code>类型的值无效（对<code>undefined</code>有效吗?）。</p>
<pre><code class="language-javascript"><span class="hljs-literal">null</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Object</span>;             <span class="hljs-comment">// false</span>

<span class="hljs-literal">true</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Boolean</span>;            <span class="hljs-comment">// false</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Boolean</span>() <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Boolean</span>;   <span class="hljs-comment">// true</span>

<span class="hljs-number">1</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Number</span>;                <span class="hljs-comment">// false</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>() <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Number</span>;     <span class="hljs-comment">// true</span>

<span class="hljs-string">&#x27;&#x27;</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">String</span>;               <span class="hljs-comment">// false</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>() <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">String</span>;     <span class="hljs-comment">// true</span>

({}) <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Object</span>;             <span class="hljs-comment">// true</span>
[] <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Array</span>;                <span class="hljs-comment">// true</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Base</span> {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Child</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">Base</span> {}
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Child</span>() <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Base</span>;        <span class="hljs-comment">// true</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Child</span>() <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Child</span>;       <span class="hljs-comment">// true</span>
</code></pre>
<p><code>instanceof</code>本质是检查对象的原型链上是否存在目标类的原型（关于原型与原型链的问题后述再行补充）。</p>
<h2 id="objectprototypetostring">Object.prototype.toString</h2>
<p><code>ES</code>的标准规定每个系统对象都有一个内部属性<code>[[Class]]</code>，这个属性值指明系统对象的类型，可通过<code>Object.prototype.toString</code>方法获取，因此此方法可准确判断所有的系统对象类型。</p>
<pre><code class="language-javascript"><span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-literal">undefined</span>);      <span class="hljs-comment">// &#x27;[object Undefined]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-literal">null</span>);           <span class="hljs-comment">// &#x27;[object Null]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-literal">true</span>);           <span class="hljs-comment">// &#x27;[object Boolean]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-number">1</span>);              <span class="hljs-comment">// &#x27;[object Number]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-title class_">BigInt</span>(<span class="hljs-number">0</span>));      <span class="hljs-comment">// &#x27;[object BigInt]&#x27;  BigInt(0) -&gt; 0n</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-string">&#x27;1&#x27;</span>);            <span class="hljs-comment">// &#x27;[object String]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-title class_">Symbol</span>());       <span class="hljs-comment">// &#x27;[object Symbol]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>({});             <span class="hljs-comment">// &#x27;[object Object]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>([]);             <span class="hljs-comment">// &#x27;[object Array]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>());     <span class="hljs-comment">// &#x27;[object Date]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-regexp">/1/</span>);            <span class="hljs-comment">// &#x27;[object RegExp]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Set</span>());      <span class="hljs-comment">// &#x27;[object Set]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Map</span>());      <span class="hljs-comment">// &#x27;[object Map]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">WeakSet</span>());  <span class="hljs-comment">// &#x27;[object WeakSet]&#x27;</span>
<span class="hljs-title class_">Object</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">toString</span>.<span class="hljs-title function_">call</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">WeakMap</span>());  <span class="hljs-comment">// &#x27;[object WeakMap]&#x27;</span>
</code></pre>
<h1 id="基本类型与引用类型">基本类型与引用类型</h1>
<p>一般把除<code>null</code>外的<code>typeof</code>返回值为<code>object</code>或<code>function</code>的类型称为引用类型（引用类型的值一般叫对象）。其它的叫基本类型或原始类型；有些地方也叫值类型，不过，个人认为值类型的叫法不太准确。</p>
<p>值类型与引用类型本质上是值的数据是存储在栈还是在堆上，一种是值的数据存在栈上，另一种是值的数据存在堆上，栈上只记录这个值在堆上的地址。前者叫值类型，后者叫引用类型。</p>
<p>赋值或函数参数传递时，编译器会把栈上的数据复制一份，因为值类型的所有数据都在栈上，所以复制的新值与原值就没有关系了，修改任何一个都对另一个没有任何影响；引用类型只是复制了在栈上记录的堆的地址，这时两个栈上地址都指向同一份内存空间，所以，修改任何一个另一个也会自动变化。</p>
<p>对于像<code>C</code>或<code>Rust</code>等系统级编程语言来说，任意类型或结构体都可以由开发者自行决定是存放在栈上还是堆上、传值还是传地址。因为栈上空间的分配，存取的速度都比堆快得多，所以对性能敏感的场合，系统编程语言是必不可少的，当然，系统级编程语言还有一个更重要的特征，就是没有自动垃圾回收器（<code>GC</code>）。</p>
<p>字符串是值类型还是引用类型没有固定的答案，对有些编程语言来说，所有的字符串都是引用类型，有些则可能短小字符串存放到栈上以提升性能。但不管怎么说，都不可能把任意字符串都存放在栈上，所以把字符串称为值类型是不准确的。</p>
<p><strong>为了方便，后述统一把<code>typeof</code>值是的<code>object</code>（除<code>null</code>外）和<code>function</code>的类型称为引用类型，其它的称为基本类型</strong></p>
<h2 id="基本类型值的封装">基本类型值的封装</h2>
<p>从前面的<code>instanceof</code>操作符得知，<code>true instanceof Boolean</code>结果为<code>false</code>，而<code>instanceof</code>操作符的本质是检查目标值的原型链上是否存在目标类型的原型对象。</p>
<pre><code class="language-javascript"><span class="hljs-literal">true</span>.<span class="hljs-property">__proto__</span> === <span class="hljs-title class_">Boolean</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>;           <span class="hljs-comment">// true</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Boolean</span>().<span class="hljs-property">__proto__</span> === <span class="hljs-title class_">Boolean</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>;  <span class="hljs-comment">// true</span>

<span class="hljs-literal">true</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Boolean</span>;                        <span class="hljs-comment">// false</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Boolean</span>() <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Boolean</span>;               <span class="hljs-comment">// true</span>
</code></pre>
<p>为什么？因为基本类型本身没有属性和方法。不过除<code>undefined</code>和<code>null</code>外，都可以像其对应的引用类型一样调用其属性与方法：</p>
<pre><code class="language-javascript"><span class="hljs-comment">// 调用布尔值的方法</span>
<span class="hljs-literal">true</span>.<span class="hljs-property">constructor</span>;       <span class="hljs-comment">// Boolean</span>
<span class="hljs-literal">true</span>.<span class="hljs-title function_">toString</span>();        <span class="hljs-comment">// &#x27;true&#x27;</span>

<span class="hljs-comment">// 调用数值的方法（注意不能写成：123.toFixed(2)，为什么?）</span>
(<span class="hljs-number">123</span>).<span class="hljs-property">constructor</span>;      <span class="hljs-comment">// Number</span>
(<span class="hljs-number">123</span>).<span class="hljs-title function_">toFixed</span>(<span class="hljs-number">2</span>);       <span class="hljs-comment">// &#x27;123.00&#x27;</span>

<span class="hljs-comment">// 调用字符串值的属性和方法</span>
<span class="hljs-string">&#x27;123&#x27;</span>.<span class="hljs-property">length</span>;           <span class="hljs-comment">// 3</span>
<span class="hljs-string">&#x27;123&#x27;</span>.<span class="hljs-title function_">padStart</span>(<span class="hljs-number">8</span>, <span class="hljs-number">0</span>);   <span class="hljs-comment">// &#x27;00000123&#x27;</span>
</code></pre>
<p>这是因为<code>JavaScript</code>会自动把<code>boolean</code>、<code>number</code>、<code>string</code>封装（box或者wrap）为其对应的临时对象并调用其属性或方法。但手动封装却不是一个好的方法：</p>
<pre><code class="language-javascript"><span class="hljs-comment">// 纯属画蛇添足，因为JavaScript引擎可以对封装进行优化，并不一定要把值复制到堆上</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(<span class="hljs-number">123</span>).<span class="hljs-title function_">toFixed</span>(<span class="hljs-number">2</span>); 
</code></pre>
<h2 id="基本类型与其对应的引用类型的区别">基本类型与其对应的引用类型的区别</h2>
<p><code>boolean</code>、<code>number</code>、<code>string</code>都有其对应的引用类型，分别对应<code>Boolean</code>、<code>Number</code>、<code>String</code>，二者在很多地方都相似，但也有不同：</p>
<pre><code class="language-javascript"><span class="hljs-number">1</span> + <span class="hljs-number">2</span> === <span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(<span class="hljs-number">1</span>) + <span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(<span class="hljs-number">2</span>);    <span class="hljs-comment">// true</span>

!!<span class="hljs-literal">false</span>;                            <span class="hljs-comment">// false</span>
!!<span class="hljs-keyword">new</span> <span class="hljs-title class_">Boolean</span>(<span class="hljs-literal">false</span>);               <span class="hljs-comment">// true</span>

<span class="hljs-keyword">typeof</span> <span class="hljs-number">1</span>;                           <span class="hljs-comment">// number</span>
<span class="hljs-keyword">typeof</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(<span class="hljs-number">1</span>);               <span class="hljs-comment">// object</span>

<span class="hljs-number">1</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Number</span>;                <span class="hljs-comment">// false</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Number</span>;    <span class="hljs-comment">// true</span>
</code></pre>
<h2 id="转为引用类型">转为引用类型</h2>
<pre><code class="language-javascript"><span class="hljs-comment">// 转为Boolean对象，x为任意值，如果不是布尔值，会根据相应规则自动转换</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Boolean</span>(x);

<span class="hljs-comment">// 转为Number对象，x为任意值，如果不是数值，会根据相应规则自动转换</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(x);

<span class="hljs-comment">// 转为String对象，x为任意值，如果不是字符串值，会根据相应规则自动转换</span>
<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(x);

<span class="hljs-comment">// 使用Object方法转换为对象（x如果是对象，则直接返回，否则转换为对应的对象类型，undefined 和 null 转换为空对象 {}）</span>
<span class="hljs-title class_">Object</span>(x);
<span class="hljs-title class_">Object</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(<span class="hljs-string">&#x27;&#x27;</span>)) <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Object</span>;   <span class="hljs-comment">// true</span>
<span class="hljs-title class_">Object</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(<span class="hljs-string">&#x27;&#x27;</span>)) <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">String</span>;   <span class="hljs-comment">// true</span>
</code></pre>
<p>实际使用中，尽量不要这么用。因为把基本类型值转换为引用类型值需要进行装箱处理，值会被复制到堆上，不仅没有好处，返而增加了内存占用及降低了性能，应该尽量避免。</p>
<h2 id="转为基本类型">转为基本类型</h2>
<p>把<code>Boolean</code>、<code>Number</code>、<code>String</code>作为函数调用时，可以把任意值强制转换为相应基本类型：</p>
<pre><code class="language-javascript"><span class="hljs-comment">// 强制转换为布尔值，x为任意值，如果不是布尔值，会根据相应规则自动转换，注意没有 new 操作符</span>
<span class="hljs-title class_">Boolean</span>(x);

<span class="hljs-comment">// 强制转换为数值，x为任意值，如果不是数值，会根据相应规则自动转换，注意没有 new 操作符</span>
<span class="hljs-title class_">Number</span>(x);

<span class="hljs-comment">// 强制转换为字符串值，x为任意值，如果不是字符串值，会根据相应规则自动转换，注意没有 new 操作符</span>
<span class="hljs-title class_">String</span>(x);
</code></pre>
<p>后述每种基于类型的详细介绍时，有更详细的说明。</p>
<h2 id="对象转换为number或string类型值的方法">对象转换为<code>number</code>或<code>string</code>类型值的方法</h2>
<p><code>Object</code>是所有对象的基类，<code>Object.prototype</code>上的<code>toString</code>和<code>valueOf</code>方法可以定制转换为<code>number</code>或<code>string</code>值，<code>toString</code>方法可把对象转换为<code>string</code>字符串，<code>valueOf</code>方法可把对象转为<code>number</code>值。</p>
<p><strong>对象转换为<code>number</code>值时的规则：</strong></p>
<ol>
<li>如果对象有<code>valueOf</code>属性且值类型为<code>function</code>，如果其返回值不是引用类型，则返回此值</li>
<li>如果对象有<code>toString</code>属性且值类型为<code>function</code>，如果其返回值不是引用类型，则返回此值</li>
<li>否则抛出异常</li>
</ol>
<pre><code class="language-javascript"><span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">1</span>, <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">3</span> });          <span class="hljs-comment">// 1</span>
<span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;2&#x27;</span>, <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">3</span> });        <span class="hljs-comment">// 2</span>
<span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> [], <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">3</span> });         <span class="hljs-comment">// 3</span>
<span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> [], <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;4&#x27;</span> });       <span class="hljs-comment">// 4</span>
<span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> [], <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> [] });        <span class="hljs-comment">// 异常</span>

<span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-number">123</span>, <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">5</span> });              <span class="hljs-comment">// 5</span>
<span class="hljs-title class_">Number</span>({ <span class="hljs-attr">valueOf</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Number</span>(), <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">6</span> });     <span class="hljs-comment">// 6</span>
</code></pre>
<p><strong>对象转换为字符串值时的规则：</strong></p>
<ol>
<li>如果对象有<code>toString</code>属性且类型为<code>function</code>，如果其返回值不是引用类型，则返回此值</li>
<li>如果对象有<code>valueOf</code>属性且类型为<code>function</code>，如果其返回值不是引用类型，则返回此值</li>
<li>否则抛出异常</li>
</ol>
<pre><code class="language-javascript"><span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;1&#x27;</span>, <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;3&#x27;</span> });    <span class="hljs-comment">// &#x27;1&#x27;</span>
<span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">2</span>, <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;3&#x27;</span> });      <span class="hljs-comment">// &#x27;2&#x27;</span>
<span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> [], <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;3&#x27;</span> });     <span class="hljs-comment">// &#x27;3&#x27;</span>
<span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> [], <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">4</span> });       <span class="hljs-comment">// &#x27;4&#x27;</span>
<span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> [], <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> [] });      <span class="hljs-comment">// 异常</span>

<span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-number">123</span>, <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">5</span> });            <span class="hljs-comment">// &#x27;5&#x27;</span>
<span class="hljs-title class_">String</span>({ <span class="hljs-attr">toString</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(), <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-number">6</span> });   <span class="hljs-comment">// &#x27;6&#x27;</span>
</code></pre>
<p>在需要字符串的场合时，按对象转换为<code>string</code>类型的规则处理，否则按转换为<code>number</code>类型的规则处理。哪些场景是按转换为<code>string</code>值的方式处理？以下代码结果是：</p>
<pre><code class="language-javascript">+ { <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;1&#x27;</span>, <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;3&#x27;</span> };              <span class="hljs-comment">// ?</span>
<span class="hljs-string">&#x27;&#x27;</span> + { <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;1&#x27;</span>, <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;3&#x27;</span> };           <span class="hljs-comment">// ?</span>
<span class="hljs-string">&#x27;&#x27;</span> + { <span class="hljs-attr">toString</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;0.00000001&#x27;</span>, <span class="hljs-attr">valueOf</span>: <span class="hljs-function">() =&gt;</span> [] };   <span class="hljs-comment">// ?</span>
</code></pre>
<p>下面的问题呢：</p>
<pre><code class="language-javascript"><span class="hljs-keyword">let</span> x = ?;

<span class="hljs-keyword">if</span> (x == <span class="hljs-number">2</span> &amp;&amp; x == <span class="hljs-number">3</span>) { 
    <span class="hljs-comment">// 什么情况下可以执行这里的代码？</span>
}

<span class="hljs-keyword">if</span> (x === <span class="hljs-number">2</span> &amp;&amp; x === <span class="hljs-number">3</span>) { 
    <span class="hljs-comment">// 什么情况下可以执行这里的代码？</span>
}
</code></pre>
<details>
<summary>答案</summary>
<pre><code class="language-javascript"><span class="hljs-keyword">const</span> x = {
    <span class="hljs-attr">value</span>: <span class="hljs-number">2</span>,
    <span class="hljs-title function_">valueOf</span>(<span class="hljs-params"></span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">value</span>++;
    }
};

<span class="hljs-keyword">if</span> (x == <span class="hljs-number">2</span> &amp;&amp; x == <span class="hljs-number">3</span>) { 
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;真的执行了&#x27;</span>);
}
</code></pre>
</details>
<h1 id="-与-">=== 与 !==</h1>
<p><code>===</code>与<code>!==</code>等同于于其它语言的<code>==</code>与<code>!=</code>，用于判断两个值是否完全相等，当两个值的类型和值都相等时，才认为两个值完全相等。因为不需要类型转换，所在效率比<code>==</code>与<code>!=</code>更高，也更容易理解代码的意图，建议尽量使用这种方式来做相等或不等判断。</p>
<h1 id="-与--1">== 与 !=</h1>
<p>在<code>JavaScript</code>中，<code>==</code>与<code>!=</code>意为近似相等，如果两个值的类型相同，则直接进行比较，不同则会自动转换为相同类型后再进行比较，这也是<code>JavaScript</code>与其它语言不太一样的地方，也是难于理解和容易犯错的地方，实际开发中应该尽量避免。</p>
<h2 id="的转换规则"><code>==</code>的转换规则</h2>
<ol>
<li>如果<code>==</code>的一边是<code>undefined</code>或<code>null</code>，另一边也是<code>undefined</code>或<code>null</code>，则相等，否则不等</li>
<li><code>false</code>等同于数值<code>0</code>，<code>true</code>等同于数值<code>1</code></li>
<li>如果<code>==</code>的一边是<code>number</code>类型
a. 如果另一边也是<code>number</code>类型，则直接比较
b. 如果另一边不是引用类型值，则转成<code>number</code>后再行比较
c. 否则按照对象转<code>number</code>类型的规则转换后再行比较</li>
<li>如果<code>==</code>的一边是<code>string</code>类型
a. 如果另一边也是<code>string</code>类型，则直接比较
b. 如果另一边不是引用类型值，则转成<code>string</code>后再行比较
c. 否则按照对象转<code>string</code>类型的规则转换后再行比较</li>
<li>地址相同则相等，否则不等</li>
</ol>
<p><code>!=</code>可以理解成<code>==</code>后再取反，所以不再详述。</p>
<h2 id="真的理解了的自动类型转换了吗">真的理解了<code>==</code>的自动类型转换了吗？</h2>
<p>看看下面的代码，能够解释为什么吗？</p>
<pre><code class="language-javascript"><span class="hljs-literal">false</span> == <span class="hljs-literal">null</span>;         <span class="hljs-comment">// false</span>
<span class="hljs-literal">false</span> == <span class="hljs-literal">undefined</span>;    <span class="hljs-comment">// false</span>
<span class="hljs-literal">false</span> == <span class="hljs-title class_">NaN</span>;          <span class="hljs-comment">// false</span>
<span class="hljs-literal">false</span> == <span class="hljs-number">0</span>;            <span class="hljs-comment">// true</span>
<span class="hljs-literal">false</span> == <span class="hljs-string">&#x27;&#x27;</span>;           <span class="hljs-comment">// true</span>
<span class="hljs-literal">false</span> == [];           <span class="hljs-comment">// true</span>
<span class="hljs-literal">false</span> == {};           <span class="hljs-comment">// false</span>

<span class="hljs-number">0</span> == <span class="hljs-literal">null</span>;             <span class="hljs-comment">// false</span>
<span class="hljs-number">0</span> == <span class="hljs-literal">undefined</span>;        <span class="hljs-comment">// false</span>
<span class="hljs-number">0</span> == <span class="hljs-title class_">NaN</span>;              <span class="hljs-comment">// false</span>
<span class="hljs-number">0</span> == [];               <span class="hljs-comment">// true</span>
<span class="hljs-number">0</span> == {};               <span class="hljs-comment">// false</span>
<span class="hljs-number">0</span> == <span class="hljs-string">&#x27;\n&#x27;</span>;             <span class="hljs-comment">// true</span>

<span class="hljs-string">&#x27;&#x27;</span> == <span class="hljs-literal">null</span>;            <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;&#x27;</span> == <span class="hljs-literal">undefined</span>;       <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;&#x27;</span> == <span class="hljs-title class_">NaN</span>;             <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;&#x27;</span> == <span class="hljs-number">0</span>;               <span class="hljs-comment">// true</span>
<span class="hljs-string">&#x27;&#x27;</span> == [];              <span class="hljs-comment">// true</span>
<span class="hljs-string">&#x27;&#x27;</span> == {};              <span class="hljs-comment">// false</span>

<span class="hljs-string">&#x27;0&#x27;</span> == <span class="hljs-literal">null</span>;           <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;0&#x27;</span> == <span class="hljs-literal">undefined</span>;      <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;0&#x27;</span> == <span class="hljs-literal">false</span>;          <span class="hljs-comment">// true</span>
<span class="hljs-string">&#x27;0&#x27;</span> == <span class="hljs-title class_">NaN</span>;            <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;0&#x27;</span> == <span class="hljs-number">0</span>;              <span class="hljs-comment">// true</span>
<span class="hljs-string">&#x27;0&#x27;</span> == <span class="hljs-string">&#x27;&#x27;</span>;             <span class="hljs-comment">// false</span>
<span class="hljs-string">&#x27;f&#x27;</span> == [<span class="hljs-string">&#x27;f&#x27;</span>];          <span class="hljs-comment">// true</span>
<span class="hljs-string">&#x27;Infinity&#x27;</span> == <span class="hljs-title class_">Infinity</span>;<span class="hljs-comment">// true</span>

[] == ![];             <span class="hljs-comment">// true</span>
<span class="hljs-number">2</span> == [<span class="hljs-number">2</span>];              <span class="hljs-comment">// true</span>
<span class="hljs-string">&#x27;&#x27;</span> == [<span class="hljs-literal">null</span>];          <span class="hljs-comment">// true</span>
</code></pre>
<p>一般情况下，都不要使用<code>==</code>或<code>!=</code>，但以下情况例外：</p>
<pre><code class="language-javascript"><span class="hljs-comment">// 判断x是 undefined 或 null</span>
<span class="hljs-keyword">if</span> (x == <span class="hljs-literal">null</span>) {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>为什么？</p>
<h1 id="-与--2">&lt; 与 &lt;=</h1>
<p>看看下面的代码：</p>
<pre><code class="language-javascript">({ <span class="hljs-attr">x</span>: <span class="hljs-number">42</span> }) &lt; { <span class="hljs-attr">x</span>: <span class="hljs-number">43</span> };  <span class="hljs-comment">// false </span>
({ <span class="hljs-attr">x</span>: <span class="hljs-number">42</span> }) &gt; { <span class="hljs-attr">x</span>: <span class="hljs-number">43</span> };  <span class="hljs-comment">// false </span>
({ <span class="hljs-attr">x</span>: <span class="hljs-number">42</span> }) == { <span class="hljs-attr">x</span>: <span class="hljs-number">43</span> }; <span class="hljs-comment">// false </span>
({ <span class="hljs-attr">x</span>: <span class="hljs-number">42</span> }) &lt;= { <span class="hljs-attr">x</span>: <span class="hljs-number">43</span> }; <span class="hljs-comment">// true </span>
({ <span class="hljs-attr">x</span>: <span class="hljs-number">42</span> }) &gt;= { <span class="hljs-attr">x</span>: <span class="hljs-number">43</span> }; <span class="hljs-comment">// true</span>
</code></pre>
<p>为什么<code>a &lt; b、a == b、a &gt; b</code>都不成立，但<code>a &gt;= b 及 a &lt;= b</code>都成立，怎么理解？</p>
<p>根据规范<code>a &lt;= b</code>被处理为<code>b &lt; a</code>，然后将结果反转，因为<code>b &lt; a === false</code>，所以<code>a &lt;= b === true</code>。</p>
<p>这与我们设想的大相径庭，即<code>&lt;=</code>应该是<code>小于或者等于</code>，但<code>JavaScript</code>中的<code>&lt;=</code>是<code>不大于</code>的意思，即：<code>a &lt;= b === !(b &lt; a)</code>。</p>
<p>相等比较有严格相等，关系比较却没有严格关系比较<code>&lt;== 或 &gt;==</code>。 也就是说如果要避免<code>a &lt; b</code>中发生隐式强制类型转换，我们只能确保<code>a</code>和<code>b</code>为相同的类型，除此之外别无他法。</p>
<p>其转换规则与<code>==</code>相同，此处不再赘述。</p>
<pre><code class="language-javascript">[<span class="hljs-number">1</span>] &lt; <span class="hljs-string">&#x27;02&#x27;</span>;     <span class="hljs-comment">// false (按字符串比较)</span>
[<span class="hljs-string">&#x27;12&#x27;</span>] &lt; <span class="hljs-number">2</span>;     <span class="hljs-comment">// false (按数字比较）</span>
</code></pre>

            
            
        </body>
        </html>